package engine

import (
	"encoding/json"
	"github.com/kataras/golog"
	"sdn-executor/core/gnmi"
	"sdn-executor/model/common"
	model "sdn-executor/model/gnmi"
	"sdn-executor/storage/sql/executor"
	"sdn-executor/utils/erros"
)

// ExecuteGNMICap 执行gnmi命令
func ExecuteGNMICap(m *Message) *model.Result {
	query := m.Request.(*model.QueryCap)
	return executeGNMI(m, &query.BaseQuery, func(client *gnmi.GNMIClient) (any, *erros.Error) {
		return client.Capabilities()
	})

}

// ExecuteGNMIGet 执行gnmi命令
func ExecuteGNMIGet(m *Message) *model.Result {
	query := m.Request.(*model.QueryGet)
	return executeGNMI(m, &query.BaseQuery, func(client *gnmi.GNMIClient) (any, *erros.Error) {
		return client.Get(query.Xpath, []string{})
	})
}

// ExecuteGNMIUpdate 执行gnmi命令
func ExecuteGNMIUpdate(m *Message) *model.Result {
	query := m.Request.(*model.QueryUpdate)
	return executeGNMI(m, &query.BaseQuery, func(client *gnmi.GNMIClient) (any, *erros.Error) {
		return client.Update(query.Xpath, query.Content)
	})

}

// ExecuteGNMIReplace 执行gnmi命令
func ExecuteGNMIReplace(m *Message) *model.Result {
	query := m.Request.(*model.QueryReplace)
	return executeGNMI(m, &query.BaseQuery, func(client *gnmi.GNMIClient) (any, *erros.Error) {
		return client.Replace(query.Xpath, query.Content)
	})

}

// ExecuteGNMIDelete 执行gnmi命令
func ExecuteGNMIDelete(m *Message) *model.Result {
	query := m.Request.(*model.QueryDelete)
	return executeGNMI(m, &query.BaseQuery, func(client *gnmi.GNMIClient) (any, *erros.Error) {
		return client.Delete(query.Xpath)
	})

}

// ExecuteGNMI 执行gnmi命令
func executeGNMI(m *Message, baseQuery *common.BaseQuery, SendAndReceive func(client *gnmi.GNMIClient) (any, *erros.Error)) *model.Result {
	requestId := m.RequestId
	err := executor.UpdateSdnExecutorResultStatusByRequestIdAndOrder(requestId, 0, string(common.ExecStatusExecuting))
	if err != nil {
		return buildGNMIFailResult(requestId, "", err)
	}

	device := baseQuery.Device

	client, err := gnmi.NewGNMIClient(device, baseQuery.Port, baseQuery.User, baseQuery.Password)
	defer client.Close()
	if err != nil {
		golog.Errorf("gnmi.NewGNMIClient failed. device: %s, requestId: %s, err: %+v", device, requestId, err)
		executor.FailSdnExecutorResult(requestId, 0, "", err.Msg)
		return buildGNMIFailResult(requestId, "", err)
	}

	response, err := SendAndReceive(client)
	results, e := json.Marshal(response)
	if err != nil {
		golog.Errorf("SendAndReceive failed. device: %s, requestId: %s, err: %+v", device, requestId, err)
		e1 := executor.FailSdnExecutorResult(requestId, 0, string(results), err.Msg)
		if e1 != nil {
			golog.Errorf("sql.FailSdnExecutorResult failed. results: %s, device: %s, requestId: %s, err: %+v",
				string(results), device, requestId, e1)
		}
		return buildGNMIFailResult(requestId, response, err)
	}

	if e != nil {
		golog.Errorf("json.Marshal failed. device: %s, requestId: %s, err: %+v", device, requestId, err)
		e1 := executor.FailSdnExecutorResult(requestId, 0, string(results), e.Error())
		if e1 != nil {
			golog.Error("sql.FailSdnExecutorResult failed. results: %s, device: %s, requestId: %s, err: %+v",
				string(results), device, requestId, e1)
		}
		return buildGNMIFailResult(requestId, response, erros.New(erros.CodeMalformedJSON, "json marshal failed"))
	}

	e1 := executor.SuccessSdnExecutorResult(requestId, 0, string(results))
	if e1 != nil {
		golog.Error("sql.SuccessSdnExecutorResult failed. results: %s, device: %s, requestId: %s, err: %+v",
			string(results), device, requestId, e1)
	}
	return buildGNMISuccessResult(requestId, response)
}

func buildGNMIFailResult(requestId string, response any, err *erros.Error) *model.Result {
	return &model.Result{
		Code:      err.Code,
		Msg:       err.Msg,
		RequestId: requestId,
		Status:    common.ExecStatusFail,
		Response:  response,
	}
}

func buildGNMISuccessResult(requestId string, response any) *model.Result {
	return &model.Result{
		Code:      erros.OK.Code,
		Msg:       erros.OK.Msg,
		RequestId: requestId,
		Status:    common.ExecStatusSuccess,
		Response:  response,
	}
}
